FFMPEG通过管道将图片推送流媒体 |
您所在的位置:网站首页 › ffmpeg 发送数据 › FFMPEG通过管道将图片推送流媒体 |
最近遇到个需求,将私有协议的码流,就是比较老的视频设备啦,新设备都支持标准H264,H265了,或者私有平台协议的视频,将这些私有协议视频通过转码推送到标准的流媒体服务器,然后通过网页不使用插件进行。目前无插件播放的播放器很多比如video.js啦,一般只支持H264的flv格式,HLS等,当然也有支持H265的,不过这些都不是重点,今天的重点是怎么通过FFMPEG将私有协议的码流转化为标准的H264然后通过FFMPEG推送到流媒体服务器,从而通过网页直接播放。 这个需求嘛,做的方式也很多,比如先对接私有SDK,然后通过私有解码库解码为YVU然后在编码转发,这是一个比较普遍的做法,也很常规。不过其实比较麻烦, 需要对编解码有一定基础。下面我介绍的方法较为简单,也很通用,只需要私有SDK支持抓图功能即可,一般私有SDK抓图的接口基本都有。 主要思路就是使用私有SDK的抓图接口,不停的抓图,然后将图片数据输送到FFMPEG的管道,这样就可以源源不断的推送了。 首先是FFMPEG的管道命令使用方法 ffmpeg.exe -f image2pipe -i \\.\\pipe\MyPipe -pix_fmt yuv420p -vcodec libx264 -f rtsp -rtsp_transport tcp "rtsp://127.0.0.1:8554/1234/1" 调用这个命令后,FFMPEG就会连接\\.\\pipe\MyPipe的命名管道,如果没有打开命名管道,FFMPEG会退出,如果有,则会等待管道数据,我们就是利用此功能进行推送 好了,废话不多说,上干货,也就是代码,这里以windows为例,linux大同小异就是管道的使用方法不同而已,大家学会举一反三
long StartProgress()//调用FFMPEG命令推流 { char sProcessID[32] = {0}; sprintf_s(sProcessID,sizeof(sProcessID),"%d",GetCurrentProcessId()); char sparam[1024]; //调用FFMPEG命令通过管道数据推流,这里想推什么流改后面的推流地址即可,FFMPEG很强大的 sprintf(sparam,"ffmpeg.exe -f image2pipe -i \\\\.\\pipe\\MyPipe -pix_fmt yuv420p -vcodec libx264 -f rtsp -rtsp_transport tcp \"rtsp://127.0.0.1:8554/1234/1\""); PROCESS_INFORMATION pi; STARTUPINFO si; memset(&si, 0, sizeof(si)); si.cb = sizeof(STARTUPINFO); si.dwFlags = STARTF_USESHOWWINDOW; si.lpTitle = ""; si.wShowWindow = SW_SHOWNORMAL; DWORD dwExitCode =0; BOOL ret = ::CreateProcess(NULL,(char*)sparam, NULL, NULL, FALSE,CREATE_NEW_CONSOLE, NULL, NULL, &si, &pi); if (ret) { //m_lpid = pi.dwProcessId; //GF_AddProcessToJobObject(pi.hProcess); //关闭子进程的主线程句柄 //等待子进程的退出 //WaitForSingleObject(pi.hProcess,INFINITE); //获取子进程的退出码 //GetExitCodeProcess(pi.hProcess, &dwExitCode); //::CloseHandle((HANDLE)pi.dwProcessId); ::CloseHandle((HANDLE)pi.hThread); //关闭子进程句柄 //CloseHandle(pi.hProcess); }else { //LOGE // add a NULL disc. ACL to the security descriptor. if (SetSecurityDescriptorDacl(&sd, TRUE, (PACL) NULL, FALSE)) { sa.nLength = sizeof(sa); sa.lpSecurityDescriptor =&sd; sa.bInheritHandle = TRUE; //创建一个命名管道,在windows中\代表zhuan'yi两个\\代表一个\ HANDLE hNamedPipe = CreateNamedPipeA("\\\\.\\pipe\\MyPipe", PIPE_ACCESS_DUPLEX | FILE_FLAG_OVERLAPPED, PIPE_TYPE_BYTE, 1, 1024, 1024,0 , &sa); //检查是否创建成功 if (hNamedPipe == INVALID_HANDLE_VALUE) { printf("create named pipe failed!\n"); } else { printf("create named pipe success!\n"); } //异步IO结构 OVERLAPPED op; ZeroMemory(&op, sizeof(OVERLAPPED)); //创建一个事件内核对象 op.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL); //等待一个客户端进行连接 BOOL b = ConnectNamedPipe(hNamedPipe, &op); //开启FFMPEG,并调用命令 StartProgress(); //当有客户端进行连接时,事件变成有信号的状态 if (WaitForSingleObject(op.hEvent, INFINITE) == 0) { printf("client connect success!\n"); } else { printf("client connect failed!\n"); } unsigned char* buff = new unsigned char[1024*1024*100]; memset(buff,0,1024*1024*100); CFile file; while(TRUE) { if(m_lPlayBackHandle == -1) return -1; //下面的函数就是私有SDK抓图函数,自己改为自己的抓图接口即可 str.Format("G:\\test\\conver\\bin\\1234\\1.bmp",num); GF_CreateDir(str); int nRes = EX_NET_CapturePicture(0,m_lPlayBackHandle,(char*)(LPCTSTR)str); //--------抓图接口抓图完成后,读取图像数据,通过管道输入到FFMPEG if(file.Open(str,CFile::modeReadWrite)) { memset(buff,0,1024*1024*100); int nLen = file.Read(buff,1024*1024*100); DWORD cbWrite; WriteFile(hNamedPipe, buff, nLen, &cbWrite, NULL); file.Close(); } Sleep(10); } } } return 0xFFFF; }
效果图上个 说明推流成功啦 VLC播放效果 CPU占用略高,FFMPEG转码占用CPU本身比较高 我I5的CPU大概解码15%,编码15%左右 此文仅用来抛砖引玉,给出个思路,具体应用到项目中,还需很多工作 |
今日新闻 |
推荐新闻 |
CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3 |